﻿using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace AuthTemplate.Infrastructure.Token
{
    public class CommonToken
    {
        private static  string _jwtSecretKey = ConfigurationManager.AppSettings["JWTSecretKey"];
        private static  int _jwtTokenValidPeriod = int.Parse(ConfigurationManager.AppSettings["JWTTokenValidPeriod"]);

        /// <summary>
        /// 通过tokenInfo获取token
        /// </summary>
        /// <param name="tokenInfo"></param>
        /// <returns></returns>
        public static string GetToken(TokenInfo tokenInfo)
        {
            var jwtCreated = GetUnixTimespan(DateTime.UtcNow);
            var jwtCreatedOver = GetUnixTimespan(DateTime.UtcNow.AddHours(_jwtTokenValidPeriod));
            var payload = new Dictionary<string, object>
                {
                    {"iss", tokenInfo.iss},//非必须。issuer 请求实体，可以是发起请求的用户的信息，也可是jwt的签发者。
                    {"iat", jwtCreated},//非必须。issued at。 token创建时间，unix时间戳格式
                    {"exp", jwtCreatedOver},//非必须。expire 指定token的生命周期。unix时间戳格式
                    {"aud", tokenInfo.aud},//非必须。接收该JWT的一方。
                    {"sub", tokenInfo.sub},//非必须。该JWT所面向的用户
                    {"jti", tokenInfo.jti},//非必须。JWT ID。针对当前token的唯一标识
                    {"UserID",tokenInfo.UserID },
                    {"UserName", tokenInfo.UserName},//自定义字段 用于存放当前登录人账户信息
                    {"UserRole", tokenInfo.UserRole},//自定义字段 用于存放当前登录人登录权限信息
                };

            IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
            IJsonSerializer serializer = new JsonNetSerializer();
            IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
            IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);

            var token = encoder.Encode(payload,_jwtSecretKey);
            return token;
        }

        /// <summary>
        /// 通过token获取payload对象（类似于TokenInfo对象）
        /// </summary>
        /// <param name="Token"></param>
        /// <param name="verify">是否检验签名</param>
        /// <returns></returns>
        public static TokenInfo DecodeToken(string Token ,bool verify = true)
        {
            try
            {
                IJsonSerializer serializer = new JsonNetSerializer();
                IDateTimeProvider provider = new UtcDateTimeProvider();
                IJwtValidator validator = new JwtValidator(serializer, provider);
                IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
                IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);

                var json = decoder.Decode(Token, _jwtSecretKey, verify);
                var payloadDic = serializer.Deserialize<Dictionary<string, object>>(json);
                TokenInfo tokenInfo = DicToObject<TokenInfo>(payloadDic);
                return tokenInfo;
            }
            catch(TokenExpiredException et)
            {
                throw new TokenExpiredException("Token 已经超时."+et.Message);
            }
            catch(SignatureVerificationException es)
            {
                throw new SignatureVerificationException("Token 具有非法签名." + es.Message);
            }
            
        }

        /// <summary>
        /// 获取Unix时间戳
        /// </summary>
        /// <param name="utcTime">必须是utc时间</param>
        /// <returns></returns>
        public static double GetUnixTimespan(DateTime utcTime)
        {
            var jwtUnixTimespan =
                Math.Round((utcTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds + 5);
            return jwtUnixTimespan;
        }


        /// <summary>
        /// 字典类型转化为对象
        /// </summary>
        /// <param name="dic"></param>
        /// <returns></returns>
        public static T DicToObject<T>(Dictionary<string, object> dic) where T : new()
        {
            T model = new T();
            PropertyInfo[] modelPro = model.GetType().GetProperties();
            if (modelPro.Length > 0 && dic.Count() > 0)
            {
                for (int i = 0; i < modelPro.Length; i++)
                {
                    if (dic.ContainsKey(modelPro[i].Name))
                    {
                        modelPro[i].SetValue(model, dic[modelPro[i].Name], null);
                    }
                }
            }
            return model;
        }
    }
}
